﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using LegendHub.Crypt;

namespace LegendHub.IO
{
    public class XmlHelper
    {
        public XmlDocument Doc;

        private XDocument _xdocument;

        private readonly int _isAes;

        public string FilePath;

        public XElement XDocRoot
        {
            get
            {
                if (_xdocument == null)
                {
                    _xdocument = GetDocument();
                }
                return _xdocument.Root;
            }
        }

        public XmlHelper(string spath, bool bCreate = false)
        {
            FilePath = string.Empty;

            FilePath = spath;
            Doc = new XmlDocument();
            try
            {
                if (!bCreate)
                {
                    Doc.Load(FilePath);
                }
            }
            catch
            {
                // ignored
            }
        }

        public XmlHelper(string spath, int isAes)
        {
            FilePath = string.Empty;

            _isAes = isAes;
            FilePath = spath;
            Doc = new XmlDocument();
            try
            {
                var streamReader = new StreamReader(spath);
                var end = streamReader.ReadToEnd();
                var str = AES.AESDecrypt(end);
                streamReader.Close();
                Doc.LoadXml(str);
            }
            catch
            {
                // ignored
            }
        }

        public XmlElement CreateElement(XmlElement pNode, string elementName)
        {
            var xmlElement = Doc.CreateElement(elementName);
            pNode.AppendChild(xmlElement);
            return xmlElement;
        }

        public XmlElement CreateElementRoot(string elementName)
        {
            var xmlElement = Doc.CreateElement(elementName);
            Doc.AppendChild(xmlElement);
            return xmlElement;
        }

        public void CreateItemsFromList<T>(XmlElement pNode, string elementName, List<T> lstItem)
        {
            var properties = typeof(T).GetProperties();
            foreach (var t in lstItem)
            {
                var xmlElement = Doc.CreateElement(elementName);
                foreach (var propertyInfo in
                    properties.Where(
                        memberInfo =>
                            memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase")))
                {
                    if (!propertyInfo.CanWrite)
                    {
                        continue;
                    }
                    var str = Doc.CreateAttribute(propertyInfo.Name);
                    var value = propertyInfo.GetValue(t, null);
                    str.Value = value != null ? value.ToString() : "";
                    value = null;
                    xmlElement.Attributes.Append(str);
                }
                pNode.AppendChild(xmlElement);
            }
        }

        public void Delete(string node, string attribute)
        {
            try
            {
                var xmlNodes = Doc.SelectSingleNode(node);
                var xmlElement = (XmlElement)xmlNodes;
                if (!attribute.Equals(""))
                {
                    if (xmlElement != null) xmlElement.RemoveAttribute(attribute);
                }
                else
                {
                    if (xmlNodes == null) return;
                    if (xmlNodes.ParentNode != null) xmlNodes.ParentNode.RemoveChild(xmlNodes);
                }
            }
            catch
            {
                // ignored
            }
        }

        public List<T> ElementsToList<T>(IEnumerable<XElement> xNodes)
        {
            var ts = new List<T>();
            var properties = typeof(T).GetProperties();
            foreach (var xNode in xNodes)
            {
                var t = (T)Activator.CreateInstance(typeof(T));
                foreach (var propertyInfo in
                    properties.Where(
                        memberInfo =>
                            memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase")))
                {
                    foreach (var xAttribute in xNode.Attributes())
                    {
                        if (!string.Equals(xAttribute.Name.ToString(), propertyInfo.Name, StringComparison.CurrentCultureIgnoreCase))
                        {
                            continue;
                        }
                        EntityHelper.SetValue(t, propertyInfo, xAttribute.Value);
                    }
                }
                ts.Add(t);
            }
            return ts;
        }

        public ObservableCollection<T> ElementsToCollection<T>(IEnumerable<XElement> xNodes)
        {
            var ts = new ObservableCollection<T>();
            var properties = typeof(T).GetProperties();
            foreach (var xNode in xNodes)
            {
                var t = (T)Activator.CreateInstance(typeof(T));
                foreach (var propertyInfo in
                    properties.Where(
                        memberInfo => memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase")))
                {
                    var info = propertyInfo;
                    foreach (var xAttribute in xNode.Attributes().Where(xAttribute => string.Equals(xAttribute.Name.ToString(), info.Name, StringComparison.CurrentCultureIgnoreCase)))
                    {
                        EntityHelper.SetValue(t, propertyInfo, xAttribute.Value);
                    }
                }
                ts.Add(t);
            }
            return ts;
        }
        public T ElementsToProp<T>(IEnumerable<XElement> xNodes, string attrName = "value")
        {
            var t = (T)Activator.CreateInstance(typeof(T));
            var properties = typeof(T).GetProperties();
            foreach (var propertyInfo in properties)
            {
                if (!propertyInfo.CanWrite) continue;
                var info = propertyInfo;
                var xElement = xNodes.FirstOrDefault(o => o.Name == info.Name);
                if (xElement != null && xElement.Attribute(attrName) != null)
                {
                    EntityHelper.SetValue(t, propertyInfo, xElement.Attribute(attrName).Value);
                }
                else if (propertyInfo.GetGetMethod() == null)
                {
                    EntityHelper.SetValueDefault(t, propertyInfo);
                }
            }
            return t;
        }

        public T ElementsToProp<T>(XElement xNode)
        {
            var t = (T)Activator.CreateInstance(typeof(T));
            var properties = typeof(T).GetProperties();
            foreach (var propertyInfo in
                properties.Where(
                    memberInfo => memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase"))
                )
            {
                Func<XAttribute, bool> name = null;
                var propertyInfo1 = propertyInfo;
                var xAttributes = xNode.Attributes();
                var info = propertyInfo;
                name = s => s.Name == info.Name;
                EntityHelper.SetValue(t, propertyInfo1, xAttributes.First(name).Value);
            }
            return t;
        }

        public XmlNode GetNode(string elementName)
        {
            return Doc.DocumentElement.SelectSingleNode(elementName);
        }

        public string GetNode(XmlNode node, string nodeName, string attribute)
        {
            var str = "";
            try
            {
                var xmlNodes = node.SelectSingleNode(nodeName);
                if (xmlNodes != null)
                {
                    if (xmlNodes.Attributes != null)
                        str = (attribute.Equals("") ? xmlNodes.InnerText : xmlNodes.Attributes[attribute].Value);
                }
                else
                {
                    return "";
                }
            }
            catch
            {
                // ignored
            }
            return str;
        }

        public string GetNode(XmlNode node, string attribute)
        {
            var str = "";
            try
            {
                if (node.Attributes != null)
                    str = (attribute.Equals("") ? node.InnerText : node.Attributes[attribute].Value);
            }
            catch 
            {
                // ignored
            }
            return str;
        }

        public XmlDocument GetXmlDocument(XDocument document)
        {
            XmlDocument xmlDocument;
            using (var xmlReader = document.CreateReader())
            {
                var xmlDocument1 = new XmlDocument();
                xmlDocument1.Load(xmlReader);
                if (document.Declaration != null)
                {
                    var xmlDeclaration = xmlDocument1.CreateXmlDeclaration(document.Declaration.Version, document.Declaration.Encoding, document.Declaration.Standalone);
                    xmlDocument1.InsertBefore(xmlDeclaration, xmlDocument1.FirstChild);
                }
                xmlDocument = xmlDocument1;
            }
            return xmlDocument;
        }

        public void Insert(string node, string element, string attribute, string value)
        {
            try
            {
                var xmlNodes = Doc.SelectSingleNode(node);
                if (!element.Equals(""))
                {
                    var xmlElement = Doc.CreateElement(element);
                    if (!attribute.Equals(""))
                    {
                        xmlElement.SetAttribute(attribute, value);
                    }
                    else
                    {
                        xmlElement.InnerText = value;
                    }
                    if (xmlNodes != null) xmlNodes.AppendChild(xmlElement);
                }
                else if (!attribute.Equals(""))
                {
                    ((XmlElement)xmlNodes).SetAttribute(attribute, value);
                }
            }
            catch
            {
                // ignored
            }
        }

        public void ListSavetoElements<T>(XmlNode pNode, List<T> lst, string elementName)
        {
            pNode.RemoveAll();
            var properties = typeof(T).GetProperties();
            foreach (var t in lst)
            {
                var xmlElement = Doc.CreateElement(elementName);
                foreach (var propertyInfo in
                    properties.Where(
                        memberInfo =>
                            memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase")))
                {
                    var str = Doc.CreateAttribute(propertyInfo.Name);
                    str.Value = propertyInfo.GetValue(t, null).ToString();
                    xmlElement.Attributes.Append(str);
                }
                pNode.AppendChild(xmlElement);
            }
        }

        private XDocument GetDocument()
        {
            _xdocument = new XDocument();
            using (var xmlWriter = _xdocument.CreateWriter())
            {
                Doc.WriteTo(xmlWriter);
            }
            var xmlDeclaration = Doc.ChildNodes.OfType<XmlDeclaration>().FirstOrDefault<XmlDeclaration>();
            if (xmlDeclaration != null)
            {
                _xdocument.Declaration = new XDeclaration(xmlDeclaration.Version, xmlDeclaration.Encoding, xmlDeclaration.Standalone);
            }
            return _xdocument;
        }

        public void PropSavetoElementAttrs<T>(XmlNode pNode, T prop)
        {
            var xmlElement = (XmlElement)pNode;
            var properties = typeof(T).GetProperties();
            foreach (var propertyInfo in
                properties.Where(
                    memberInfo => memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase"))
                )
            {
                xmlElement.SetAttribute(propertyInfo.Name, propertyInfo.GetValue(prop, null).ToString());
            }
        }

        public void PropSavetoElements<T>(XmlNode pNode, T prop, string attrName = "value")
        {
            var properties = typeof(T).GetProperties();
            foreach (var propertyInfo in
                from memberInfo in properties
                where memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase")
                select memberInfo)
            {
                var xmlNodes = pNode.SelectSingleNode(propertyInfo.Name) ?? pNode.AppendChild(pNode.OwnerDocument.CreateElement(propertyInfo.Name));
                ((XmlElement)xmlNodes).SetAttribute(attrName, propertyInfo.GetValue(prop, null).ToString());
            }
        }

        public string Read(string node, string attribute)
        {
            var str = "";
            try
            {
                var xmlNodes = Doc.SelectSingleNode(node);
                if (xmlNodes != null)
                {
                    str = (attribute.Equals("") ? xmlNodes.InnerText : xmlNodes.Attributes[attribute].Value);
                }
                else
                {
                    return "";
                }
            }
            catch
            {
                // ignored
            }
            return str;
        }

        public void Save(bool isLinq = false)
        {
            try
            {
                if (_isAes == 1)
                {
                    var str = AES.AESEncrypt((isLinq ? _xdocument.ToString() : Doc.InnerXml));
                    using (var streamWriter = new StreamWriter(FilePath, false))
                    {
                        streamWriter.Write(str);
                        streamWriter.Close();
                    }
                }
                else if (!isLinq)
                {
                    Doc.Save(FilePath);
                }
                else
                {
                    _xdocument.Save(FilePath);
                }
            }
            catch
            {
                // ignored
            }
        }

        public void Update(string node, string attribute, string value)
        {
            try
            {
                var xmlDocument = new XmlDocument();
                xmlDocument.Load(FilePath);
                var xmlElement = (XmlElement)xmlDocument.SelectSingleNode(node);
                if (!attribute.Equals(""))
                {
                    if (xmlElement != null) xmlElement.SetAttribute(attribute, value);
                }
                else
                {
                    if (xmlElement != null) xmlElement.InnerText = value;
                }
            }
            catch
            {
                // ignored
            }
        }

        public XElement XElement(string sNodeName)
        {
            if (_xdocument == null)
            {
                _xdocument = GetDocument();
            }

            return _xdocument.Root.Element(sNodeName);

        }
        public IEnumerable<XElement> RootXElements()
        {
            if (_xdocument == null)
            {
                _xdocument = GetDocument();
            }

            return _xdocument.Root.Elements();

        }
        public List<T> XmlToList<T>(XmlNode xNode)
        {
            var ts = new List<T>();
            var properties = typeof(T).GetProperties();
            foreach (XmlNode childNode in xNode.ChildNodes)
            {
                var t = (T)Activator.CreateInstance(typeof(T));
                foreach (var propertyInfo in
                    properties.Where(
                        memberInfo =>
                            memberInfo.DeclaringType != null && !memberInfo.DeclaringType.Name.Equals("ModelBase")))
                {
                    if (childNode.Attributes == null) continue;
                    var info = propertyInfo;
                    foreach (var attribute in childNode.Attributes.Cast<XmlAttribute>().Where(attribute => string.Equals(attribute.Name, info.Name, StringComparison.CurrentCultureIgnoreCase)))
                    {
                        EntityHelper.SetValue(t, propertyInfo, attribute.Value);
                    }
                }
                ts.Add(t);
            }
            return ts;
        }

    }
}
